#include "library.h"
#include <QTimer>
#include <QFileInfo>
#include <QDir>
#include <QDomDocument>
#include <QUrl>
#include <QImageReader>
#include <QDebug>
#include <QDirIterator>
#include <QMutexLocker>
#include <QProcess>
#include "mediaitemimpl.h"
#include "imageloader.h"
#if defined(_WIN32)
#include <windows.h>
#endif

Library::Library()
    : m_quit(false)
{
    start();
}

Library::~Library()
{
    m_mutex.lock();
    m_quit = true;
    m_cond.wakeAll();
    m_mutex.unlock();
    wait();
}

void Library::setRootPath(const QString & path)
{
    m_root = path;
    if(!m_root.isEmpty() && m_root[m_root.length()-1] == '/')
        m_root = m_root.left(m_root.length()-1);
}

QString Library::rootPath()
{
    return m_root;
}

void Library::update()
{
    deleteEmptyDirectories(m_root, 2);
    update(m_root);
}

void Library::update(QString path)
{
    if(scanning())
        return;

    if(!path.startsWith(m_root))
        return;

    emit status(tr("updating library..."));

    QMutexLocker lock(&m_mutex);

    m_added = m_updated = m_removed = m_dirs = 0;
    m_queue.clear();

    ImageLoader loader;

    QDirIterator it(path, QDirIterator::Subdirectories);
    while(it.hasNext()) {
        it.next();
        QString filename = it.fileName();
        QFileInfo fi = it.fileInfo();
        if(fi.isFile() && loader.supports(fi.suffix().toLower())) {
            m_queue << fi.absoluteFilePath();
        }
    }
    QSet<QString> set = m_queue.toSet();
    QList<QSharedPointer<MediaItem> > items = m_items.values();
    foreach(QSharedPointer<MediaItem> item, items) {
        if(item->filename().startsWith(path) && !set.contains(item->filename())) {
            remove(item);
            m_removed++;
        }
        else if(!item->filename().startsWith(m_root)) {
            remove(item);
            m_removed++;
        }
    }

    m_cond.wakeAll();
}

bool Library::scanning()
{
    QMutexLocker lock(&m_mutex);
    return !m_queue.isEmpty();
}

QList<QSharedPointer<MediaItem> > Library::items()
{
    return m_items.values();
}

QStringList Library::folders()
{
    QDir dir(m_root);
    QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    QMutableStringListIterator dirit(dirs);
    while(dirit.hasNext()) {
        QString str = dirit.next();
        if(str.length() == 4 && str[0].isDigit() && str[1].isDigit() && str[2].isDigit() && str[3].isDigit())
            dirit.remove();
    }
    return dirs;
}

QSet<QSharedPointer<MediaItem> > Library::set(QSharedPointer<MediaItem>item)
{
    return m_itemgroups[m_group[item]];
}

void Library::collectKeywords(QSharedPointer<MediaItem> item)
{
    QSet<QString> keywords = item->tags();
    foreach(QString k, keywords) {
        QSharedPointer<Keyword> keyword;
        foreach(QSharedPointer<Keyword> i, m_keywords) {
            if(i->keyword() == k) {
                keyword = i;
                break;
            }
        }
        if(keyword.isNull()) {
            m_keywords.append(QSharedPointer<Keyword>(new Keyword(k)));
            emit keywordsChanged();
        }
    }
}

void Library::move(const QList<QUrl> & urls, const QString & target)
{
    for(int i=0; i<urls.size(); i++) {
        QString oldFilename = urls[i].toLocalFile();
        if(!m_items.contains(oldFilename))
            continue;
        QSharedPointer<MediaItem> item = m_items[oldFilename];

        QString newFilename = m_root;
        if(!target.isEmpty())
            newFilename += "/" + target;
        if(!item->created().isNull()) {
            if(target == item->created().toString("yyyy"))
                newFilename += item->created().toString("/MM/dd");
            else
                newFilename += item->created().toString("/yyyy/MM/dd");
        }

        QFileInfo fi(oldFilename);
        newFilename += "/" + fi.fileName();

        if(item->impl()->rename(newFilename)) {
            m_items.remove(oldFilename);
            m_items.insert(newFilename, item);
            QSet<QSharedPointer<MediaItem> > set = m_itemgroups[m_group[item]];
            modified(item);
            set += m_itemgroups[m_group[item]];
            foreach(QSharedPointer<MediaItem> item, set)
                emit updated(item);
        }
    }
}

void Library::add(const QString &filename)
{
    QFileInfo fi(filename);

    ImageLoader loader;
    if(loader.supports(fi.suffix().toLower())) {
        QSharedPointer<MediaItem> item(new MediaItemImpl(m_root, fi));
        add(item);
    }
}

void Library::remove(QSharedPointer<MediaItem> item)
{
    QString grp = m_group[item];
    m_itemgroups[grp] -= item;
    if(m_itemgroups[grp].isEmpty())
        m_itemgroups.remove(grp);
    m_items.remove(item->filename());
    emit removed(item);
}

void Library::erase(QSharedPointer<MediaItem> item)
{
}

void Library::update(QSharedPointer<MediaItem>item)
{
    QFileInfo fi(item->filename());
    item->impl()->update(fi);
    modified(item);
    collectKeywords(item);
    emit updated(item);
}

void Library::add(QSharedPointer<MediaItem> item)
{
    m_mutex.lock();
    m_items.insert(item->filename(), item);

    QString grp = group(item);
    m_itemgroups[grp] += item;
    m_group[item] = grp;

    m_mutex.unlock();
    collectKeywords(item);
    emit added(item);
}

void Library::modified(QSharedPointer<MediaItem>item)
{
    QString grp = m_group[item];
    QString ngrp = group(item);
    if(ngrp != grp) {
        m_itemgroups[grp] -= item;
        m_itemgroups[ngrp] += item;
        m_group[item] = ngrp;
    }
}

void Library::deleteEmptyDirectories(QString path, int mindepth)
{
    QDir dir(path);
    QStringList subdirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    foreach(QString subdir, subdirs) {
        deleteEmptyDirectories(path + "/" + subdir, mindepth - 1);
        if(mindepth <= 1)
            dir.rmdir(subdir);
    }
}

void Library::run()
{
    m_mutex.lock();
    while(!m_quit) {
        if(m_queue.isEmpty()) {
            m_cond.wait(&m_mutex);
            continue;
        }

        QString f = m_queue.takeFirst();
        QFileInfo fi(f);
        if(m_items.contains(f)) {
            QSharedPointer<MediaItem> item = m_items[f];
            if(fi.lastModified() != item->modified() || fi.size() != item->size()) {
                qint64 a = fi.size();
                qint64 b = item->size();
                QDateTime c = fi.lastModified();
                QDateTime d = item->modified();
                m_mutex.unlock();
                update(item);
                m_mutex.lock();
                m_updated++;
            }
        }
        // item does not exist, new item
        else {
            int size = m_items.size();
            m_mutex.unlock();
            add(fi.filePath());
            m_mutex.lock();
            if(m_items.size() > size)
                m_added++;
        }
        m_dirs++;
        if(m_dirs % 20 == 0) {
            m_mutex.unlock();
            emit status(QString(tr("%1 files scanned")).arg(m_dirs));
            m_mutex.lock();
        }

        if(m_queue.isEmpty()) {
            m_mutex.unlock();
            emit status(QString(tr("scan completed (%1 added, %2 updated, %3 removed).")).arg(m_added).arg(m_updated).arg(m_removed));
            m_mutex.lock();
        }
    }
    m_mutex.unlock();
}

QString Library::group(QSharedPointer<MediaItem> item)
{
    QFileInfo fi(item->filename());
    QString basename = fi.absolutePath() + "/" + fi.baseName();

    return basename + "|" + item->created().toString();
}
